package app

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	consts "qrsj-assistant/backend/const"
	"qrsj-assistant/backend/system"
	"qrsj-assistant/backend/types"
	"qrsj-assistant/backend/utils"
	"strconv"
	"sync"
	"time"
	"unsafe"

	jsoniter "github.com/json-iterator/go"
	"github.com/wailsapp/wails/v2/pkg/runtime"
)

// App struct
type App struct {
	ctx                context.Context
	json               jsoniter.API
	addressInfo        types.AddressFile
	coordBaseAddress   uintptr   // 人物坐标基址
	coordOffsets       []uintptr // 人物x坐标偏移
	tempCoordTablePath string    // 临时坐标文件路径
	cancelFunc         context.CancelFunc
	mu                 sync.Mutex
	currentHwnd        uintptr
}

type CoordInfo struct {
	X float32 `json:"x"`
	Y float32 `json:"y"`
	Z float32 `json:"z"`
}

// NewMainApp creates a new App application struct
func NewMainApp() *App {
	return &App{}
}

// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) Startup(ctx context.Context) {
	a.ctx = ctx
	a.json = jsoniter.ConfigCompatibleWithStandardLibrary
	// 配置初始化
	a.ReadConfigFile()
	// utils.ReadAddressJSON(a.json, &a.addressInfo)
	// 执行定时任务
	a.IntervalReadCoordJob(10)
}

func (a *App) Shutdown(ctx context.Context) {
	var conf types.ConfigApp
	if a.tempCoordTablePath != "" {
		conf.DefaultCoordTablePath = a.tempCoordTablePath
	}
	confJson, _ := a.json.MarshalIndent(conf, "", "  ")
	utils.WriteFile(filepath.Join(utils.GetConfigAppPath(), consts.CONFIG_FILE_NAME), confJson)
	a.StopTask()
}

// 通过类型和窗口名获取窗口句柄
func (a *App) GetWindowsHwnd(className string, windowName string) uintptr {
	hwnd, err := system.FindWindow(className, windowName)
	if err != nil {
		return 0
	}
	return uintptr(hwnd)
}

// 通过类型和窗口名获取进程ID
func (a *App) GetWindowsProcessID(className string, windowName string) int {
	return system.FindWindowProcessID(className, windowName)
}

// 获取所有窗口
func (a *App) GetAllWindows(className string, windowName string) string {
	var windowsList []system.WindowInfo
	windowsList, _ = system.FindAllWindows(className, windowName)
	data, err := a.json.Marshal(&windowsList)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
		return `[]`
	}
	return string(data)
}

// 激活窗口
func (a *App) HandleActivateWindow(hwnd uintptr) {
	system.ActivateWindow(hwnd)
}

// 绘制窗口矩形
func (a *App) HandleDrawWindowRectangle(hwnd uintptr, color uint32, duration int, interval int) {
	a.currentHwnd = hwnd
	// 先激活窗口
	// a.HandleActivateWindow(hwnd)
	time.Sleep(100 * time.Millisecond)
	if duration == 0 {
		duration = 500
	}
	if interval == 0 {
		interval = 20
	}
	durationTime := time.Duration(duration) * time.Millisecond
	intervalTime := time.Duration(interval) * time.Millisecond

	ticker := time.NewTicker(intervalTime)
	defer ticker.Stop()
	timer := time.NewTimer(durationTime)
	defer timer.Stop()

	var wg sync.WaitGroup
	wg.Add(1)

	go func() {
		defer wg.Done()
		for {
			select {
			case <-ticker.C:
				system.DrawWindowRectangle(hwnd, 3, color, true)
			case <-timer.C:
				return
			}
		}
	}()
	wg.Wait()
}

// 根据坐标地址修改人物坐标
func (a *App) WriteCharacterCoord(hwnd uintptr, x float32, y float32, z float32) bool {
	if hwnd == 0 {
		hwnd = a.currentHwnd
	}
	var err error
	address, err := system.ReadMultiLevelPointerAddress(hwnd, a.coordBaseAddress, a.coordOffsets, true)
	if err != nil {
		fmt.Printf("人物坐标地址获取失败 err=%v\n", err)
		return false
	}
	// 声明一个map, key为address地址, value为数据
	addresses := []uintptr{
		address,
		address + 8,
		address + 4,
	}
	values := []float32{x, y, z}
	err = system.WriteMemoryValues(hwnd, addresses, values)
	if err != nil {
		fmt.Printf("坐标写入错误 err=%v\n", err)
		return false
	}
	return true
}

// 获取人物坐标基址
func (a *App) GetCharacterBaseAddress(hwnd uintptr) error {
	var offsets []uintptr
	a.StopTask()
	// utils.ReadAddressJSON(a.json, &a.addressInfo)
	// []uintptr{0x48, 0x0, 0xB8, 0xD8, 0xF8, 0x160}
	for _, offset := range a.addressInfo.CoordInfo.OffsetsStr {
		offsetUint, _ := strconv.ParseUint(offset, 16, 64)
		offsets = append(offsets, uintptr(offsetUint))
	}
	a.coordOffsets = offsets
	pattern := a.addressInfo.CoordInfo.PatternBase
	var relative uintptr
	relativeUint, _ := strconv.ParseUint(a.addressInfo.CoordInfo.Relative, 16, 64)
	if a.addressInfo.CoordInfo.IsAdd {
		relative = uintptr(relativeUint)
	} else {
		relative = ^uintptr(relativeUint) + 1
	}
	baseAddress, err := system.GetBaseAddress(hwnd, offsets, pattern, relative)
	fmt.Printf("baseAddress: %x\n", baseAddress)
	if err != nil {
		return fmt.Errorf("人物坐标基址获取失败\nerr=%v", err)
	}
	if baseAddress == 0 {
		return fmt.Errorf("人物坐标基址获取失败")
	}
	a.coordBaseAddress = baseAddress
	a.addressInfo.CoordInfo.BaseAddressStr = fmt.Sprintf("%x", baseAddress)
	// address, err := system.ReadMultiLevelPointerAddress(hwnd, baseAddress, offsets, true)
	// if err != nil {
	// 	return fmt.Errorf("人物坐标地址获取失败\nerr=%v", err)
	// }
	// a.addressInfo.CoordInfo.Address = address
	utils.UpdateAddressJSON(a.json, &a.addressInfo)
	a.IntervalReadCoordJob(10)
	return nil
}

// 读取人物坐标
func (a *App) ReadCharacterCoord(hwnd uintptr) string {
	address, err := system.ReadMultiLevelPointerAddress(hwnd, a.coordBaseAddress, a.coordOffsets, true)
	if err != nil {
		fmt.Printf("人物坐标地址获取失败 err=%v\n", err)
		return "{}"
	}
	xPtr, err := system.ReadMemoryValues(hwnd, address)
	if err != nil {
		fmt.Printf("x坐标读取错误 err=%v\n", err)
		return "{}"
	}
	yPtr, err := system.ReadMemoryValues(hwnd, address+8)
	if err != nil {
		fmt.Printf("y坐标读取错误 err=%v\n", err)
		return "{}"
	}
	zPtr, err := system.ReadMemoryValues(hwnd, address+4)
	if err != nil {
		fmt.Printf("z坐标读取错误 err=%v\n", err)
		return "{}"
	}
	coordInfo := CoordInfo{
		X: *(*float32)(unsafe.Pointer(&xPtr)),
		Y: *(*float32)(unsafe.Pointer(&yPtr)),
		Z: *(*float32)(unsafe.Pointer(&zPtr)),
	}
	data, err := a.json.Marshal(&coordInfo)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
		return "{}"
	}
	return string(data)
}

// 导出人物坐标列表
func (a *App) ExportCharacterCoordList(coordTableList string) string {
	dir, _ := os.Getwd()
	if a.tempCoordTablePath != "" {
		dir = filepath.Dir(a.tempCoordTablePath)
	}
	path, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
		DefaultDirectory: dir,
		DefaultFilename:  "坐标列表备份.json",
		Title:            "导出人物坐标列表",
		Filters: []runtime.FileFilter{
			{
				DisplayName: "JSON文件",
				Pattern:     "*.json",
			},
		},
	})
	if err != nil {
		fmt.Printf("打开文件对话框失败 err=%v\n", err)
		return "打开文件对话框失败"
	}
	if path == "" {
		return "已取消"
	}
	a.tempCoordTablePath = path
	err = utils.WriteFile(path, []byte(coordTableList))
	if err != nil {
		fmt.Printf("写入文件失败 err=%v\n", err)
		return "写入文件失败"
	}
	return ""
}

// 导入人物坐标列表
func (a *App) ImportCharacterCoordList() (string, string) {
	dir, _ := os.Getwd()
	if a.tempCoordTablePath != "" {
		dir = filepath.Dir(a.tempCoordTablePath)
	}
	path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
		DefaultDirectory: dir,
		Title:            "导入人物坐标",
		Filters: []runtime.FileFilter{
			{
				DisplayName: "JSON文件",
				Pattern:     "*.json",
			},
		},
	})
	if err != nil {
		fmt.Printf("打开文件对话框失败 err=%v\n", err)
		return "[]", "打开文件对话框失败"
	}
	if path == "" {
		return "", ""
	}
	fmt.Printf("path: %v\n", filepath.Dir(path))
	a.tempCoordTablePath = path
	dataByte, err := utils.ReadFile(path)
	if err != nil {
		fmt.Printf("读取文件失败 err=%v\n", err)
		return "[]", "读取文件失败"
	}
	fmt.Printf("dataByte: %v\n", string(dataByte))
	return string(dataByte), ""
}

// 读取配置文件
func (a *App) ReadConfigFile() error {
	configAppPath := utils.InitConfigPath()
	fullFilePath := filepath.Join(configAppPath, consts.CONFIG_FILE_NAME)
	exists := utils.FileExists(fullFilePath)
	conf := types.ConfigApp{}
	if !exists {
		jsonData, _ := a.json.MarshalIndent(conf, "", "  ")
		err := utils.WriteFile(fullFilePath, jsonData)
		if err != nil {
			return fmt.Errorf("应用首次写入配置文件失败\nerr=%v", err)
		}
	} else {
		bytesData, _ := utils.ReadFile(fullFilePath)
		err := a.json.Unmarshal(bytesData, &conf)
		if err != nil {
			return fmt.Errorf("读取配置文件失败\nerr=%v", err)
		}
		a.tempCoordTablePath = conf.DefaultCoordTablePath
	}
	return nil
}

// 读取坐标文件
func (a *App) ReadCoordTableFile() (string, string) {
	if a.tempCoordTablePath == "" {
		return "[]", ""
	}
	dataByte, err := utils.ReadFile(a.tempCoordTablePath)
	if err != nil {
		return "[]", err.Error()
	}
	return string(dataByte), ""
}

// 定时读取人物坐标
func (a *App) IntervalReadCoordJob(interval time.Duration) {
	fmt.Println("定时读取人物坐标任务已启动")
	a.mu.Lock()
	ctx, cancel := context.WithCancel(context.Background())
	a.cancelFunc = cancel
	a.mu.Unlock()

	go func() {
		ticker := time.NewTicker(interval * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				utils.ReadAddressJSON(a.json, &a.addressInfo)
				var offsets []uintptr
				for _, offset := range a.addressInfo.CoordInfo.OffsetsStr {
					offsetUint, _ := strconv.ParseUint(offset, 16, 64)
					offsets = append(offsets, uintptr(offsetUint))
				}
				a.coordOffsets = offsets
				value, _ := strconv.ParseUint(a.addressInfo.CoordInfo.BaseAddressStr, 16, 64)
				a.coordBaseAddress = uintptr(value)
			case <-ctx.Done():
				return
			}
		}
	}()
}

func (a *App) StopTask() {
	a.mu.Lock()
	defer a.mu.Unlock()
	if a.cancelFunc != nil {
		a.cancelFunc()
		a.cancelFunc = nil
	}
	fmt.Println("定时读取人物坐标任务已停止")
}
